﻿//++
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
// Module Name:
//
//  VoiceMessageSender.cs
//    
// Abstract:
//
//  This module is an implementation of IMessageSender for delivering NotificationItems
//  via voice.
//  
//--
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Workflow.Runtime;
using Microsoft.OfficeCommunicationsServer.Applications.Common;
using Microsoft.Rtc.Collaboration;
using Microsoft.Rtc.Collaboration.AudioVideo;

namespace Microsoft.EnhancedReminders.Dispatcher.Workflow
{
    /// <summary>
    /// Implementation of IMessageSender that uses voice (e.g. a phone call) as the delivery
    /// channel
    /// </summary>
    [StopPoint(true)]
    public class VoiceMessageSender : WorkflowMessageSender
    {
        /// <summary>
        /// ctor.
        /// </summary>
        /// <param name="conversation">The Conversation object to use for the call</param>
        /// <param name="runtime">The WorkflowRuntime to use for hosting the workflow used for the call</param>
        /// <param name="message">The message to be delivered</param>
        public VoiceMessageSender(Conversation conversation, WorkflowRuntime runtime, NotificationItem message) :
            base(conversation, runtime, message) 
        { 
        }

        /// <summary>
        /// The name of the temporary file to use for voicemail reminders
        /// </summary>
        public string TemporaryVoicemailAudioPath
        {
            get;
            set;
        }

        /// <summary>
        /// Initialize the AudioVideoCall. 
        /// </summary>
        /// <remarks>This may throw RealTimeException if OCS is not set up for making outbound calls</remarks>
        protected override bool InitializeCall()
        {
            Logger.WriteTraceInformation(_message.Context, "Initializing AudioVideoCall");

            _call = new AudioVideoCall(_conversation);

            VoicemailNotificationItem notification = _message as VoicemailNotificationItem;
            if (notification != null)
            {
                // if initializing the audio for a voicemail notification fails, don't bother
                // making the call.
                if (!InitializeVoicemailNotification(notification, TemporaryVoicemailAudioPath))
                {
                    OnDeliveryCompleted(new NotificationResult(notification, NotificationResultStatus.Error, NotificationChannelType.Phone));
                    return false;
                }
            }

            return true;
        }

        /// <summary>
        /// Cleans up the temporary files for voicemail notifications if needed.
        /// </summary>
        protected override void CleanupCall()
        {
            Debug.Assert(_message != null, "_message != null");

            if (_message is VoicemailNotificationItem)
            {
                CleanupVoicemailNotification();
            }
        }

        /// <summary>
        /// Sets the HowContacted field on the notification result and then fires
        /// the DeliveryCompleted callback.
        /// </summary>
        /// <param name="result"></param>
        protected override void OnDeliveryCompleted(NotificationResult result)
        {
            result.HowContacted = NotificationChannelType.Phone;
            base.OnDeliveryCompleted(result);
        }

        /// <summary>
        /// If we're delivering a voicemail notification, then we need to write the raw wma bytes we
        /// were given by the Scheduler to disk and then convert the file to wav so that the speech
        /// synthesizer can play it.
        /// </summary>
        /// <param name="notification">VoicemailNotificationItem with the audio we need to write to disk</param>
        /// <param name="path">The path to write the audio to</param>
        /// <returns>False if an error was encountered and we should stop processing the notification</returns>
        private static bool InitializeVoicemailNotification(VoicemailNotificationItem notification, string path)
        {            
            bool success = false;
            string wmaPath = path;

            try
            {
                if (notification.VoicemailAudio.Length < Constants.MaxAudioFileSizeInBytes)
                {
                    File.WriteAllBytes(wmaPath, notification.VoicemailAudio);
                    
                    success = true;
                }
            }
            catch (DirectoryNotFoundException e)
            {
                OperationalLogs.LogScratchDirectoryWriteError(path, e);
            }
            catch (UnauthorizedAccessException e)
            {
                OperationalLogs.LogScratchDirectoryWriteError(path, e);
            }
            catch (FileNotFoundException e)
            {
                Logger.WriteTraceError(notification.Context, e, "FileNotFound exception thrown.");
            }
            catch (IOException e)
            {
                OperationalLogs.LogScratchDirectoryWriteError(path, e);
            }
            catch (DllNotFoundException e)
            {
                OperationalLogs.LogWindowsMediaEncoderNotFoundError(e);
            }
            catch (COMException e)
            {
                OperationalLogs.LogWindowsMediaEncoderFailedError(e);
            }
            finally
            {
                if (File.Exists(wmaPath))
                {
                    //DeleteFile(wmaPath);
                }
            }

            return success;
        }

        /// <summary>
        /// When a voicemail notification is complete, we need to delete the wma and wav files.
        /// </summary>
        private void CleanupVoicemailNotification()
        {
            Debug.Assert(TemporaryVoicemailAudioPath != null, "TemporaryVoicemailAudioPath != null");

            DeleteFile(TemporaryVoicemailAudioPath);
        }

        /// <summary>
        /// Deletes a temporary file used by the VoiceMessageSender. If any I/O
        /// errors are encountered we will log an error message, but will not do
        /// take any further action.
        /// </summary>
        private static void DeleteFile(string path)
        {
            try
            {
                if (File.Exists(path))
                {
                    File.Delete(path);
                }
            }
            catch (DirectoryNotFoundException e)
            {
                OperationalLogs.LogScratchDirectoryDeleteWarning(path, e);
            }
            catch (PathTooLongException e)
            {
                OperationalLogs.LogScratchDirectoryDeleteWarning(path, e);
            }
            catch (UnauthorizedAccessException e)
            {
                OperationalLogs.LogScratchDirectoryDeleteWarning(path, e);
            }
            catch (IOException e)
            {
                OperationalLogs.LogScratchDirectoryDeleteWarning(path, e);
            }
        }

        /// <summary>
        /// Get the Type object for the Workflow class this sender should use. This varies depending on
        /// whether or not this is a Voicemail notification or a Calendar notification.
        /// </summary>
        protected override Type WorkflowType
        {
            get
            {
                if (_message is VoicemailNotificationItem)
                {
                    return typeof(VoicemailReminder);
                }
                else if (_message is CalendarNotificationItem)
                {
                    if (_message.IsJoinNow)
                    {
                        return typeof(JoinNowVoiceReminder);
                    }
                    else
                    {
                        return typeof(MeetingReminder);
                    }
                }
                else
                {
                    Debug.Fail("Unsupported notification category encountered.");
                    Logger.WriteTraceError(_message.Context, "Unknown notification type reached. " + _message.GetType());
                    return null;
                }
            }
        }

        /// <summary>
        /// Returns the parameters we should pass to workflow. If this is a voicemail notification then we need to give
        /// Workflow the path to the voicemail audio.
        /// </summary>
        /// <returns></returns>
        protected override Dictionary<string, object> GenerateWorkflowParameters()
        {
            Dictionary<string, object> parameters = base.GenerateWorkflowParameters();

            if (_message is VoicemailNotificationItem)
            {
                TemporaryVoicemailAudioPath = Path.Combine(DispatcherHelpers.ScratchDirectory, Guid.NewGuid().ToString() + ".wma");
                parameters.Add("TemporaryVoicemailAudioPath", TemporaryVoicemailAudioPath);
            }

            return parameters;
        }



       
    }
}